using System.Text;
using System.IO;
using System.Net;
using System;
using System.Collections.Generic;
using System.Web;
using System.Collections.Specialized;

namespace Pay.Core.AlipayWap {
	/// <summary>
	/// 类名：Notify
	/// 功能：支付宝通知处理类
	/// 详细：处理支付宝各接口通知返回
	/// 版本：3.3
	/// 修改日期：2011-07-05
	/// '说明：
	/// 以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
	/// 该代码仅供学习和研究支付宝接口使用，只是提供一个参考。
	/// 
	/// //////////////////////注意/////////////////////////////
	/// 调试通知返回时，可查看或改写log日志的写入TXT里的数据，来检查通知返回是否正常 
	/// </summary>
	public class AlipayNotify {
		#region 字段
		private string _partner = "";               //合作身份者ID
		private string _key = "";                   //MD5->安全检验码,RSA->支付宝的公钥
		private string _input_charset = "utf-8";         //编码格式
		private string _sign_type = "";             //签名方式

		//支付宝消息验证地址
		private string Https_veryfy_url = "https://mapi.alipay.com/gateway.do?service=notify_verify&";
		#endregion
		private payment_query _payment_query;

		/// <summary>
		/// 构造函数
		/// 从配置文件中初始化变量
		/// </summary>
		public AlipayNotify(payment_query payment_query) {
			_payment_query = payment_query;
			//初始化基础配置信息
			_sign_type = _payment_query.key.ToUpper();
			_partner = _payment_query.app_id;
			if (_sign_type == "MD5")
				_key = _payment_query.app_secret;
			else if (_sign_type == "RSA")
				_key = HttpContext.Current.Server.MapPath("~/App_Data/key/alipay_public_key.pem");
		}

		/// <summary>
		/// 从文件读取公钥转公钥字符串
		/// </summary>
		/// <param name="Path">公钥文件路径</param>
		public static string getPublicKeyStr(string Path) {
			StreamReader sr = new StreamReader(Path);
			string pubkey = sr.ReadToEnd();
			sr.Close();
			if (pubkey != null) {
				pubkey = pubkey.Replace("-----BEGIN PUBLIC KEY-----", "");
				pubkey = pubkey.Replace("-----END PUBLIC KEY-----", "");
				pubkey = pubkey.Replace("\r", "");
				pubkey = pubkey.Replace("\n", "");
			}
			return pubkey;
		}

		/// <summary>
		///  验证消息是否是支付宝发出的合法消息
		/// </summary>
		/// <param name="notify_id">通知验证ID</param>
		/// <param name="sign">支付宝生成的签名结果</param>
		/// <returns>验证结果</returns>
		public bool Verify(string notify_id, string sign) {
			//获取返回时的签名验证结果
			var inputPara = GetRequestPost();//通知返回参数数组
			bool isSign = GetSignVeryfy(inputPara, sign);
			//获取是否是支付宝服务器发来的请求的验证结果
			string responseTxt = "false";
			if (notify_id != null && notify_id != "") { responseTxt = GetResponseTxt(notify_id); }

			//写日志记录（若要调试，请取消下面两行注释）
			//string sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign.ToString() + "\n 返回回来的参数：" + GetPreSignStr(inputPara) + "\n ";
			//Core.LogResult(sWord);

			//判断responsetTxt是否为true，isSign是否为true
			//responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
			//isSign不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
			if (responseTxt == "true" && isSign)//验证成功
			{
				return true;
			} else//验证失败
			  {
				return false;
			}
		}

		/// <summary>
		/// 获取支付宝POST过来通知消息，并以“参数名=参数值”的形式组成数组
		/// </summary>
		/// <returns>request回来的信息组成的数组</returns>
		public SortedDictionary<string, string> GetRequestPost() {
			int i = 0;
			SortedDictionary<string, string> sArray = new SortedDictionary<string, string>();
			NameValueCollection coll;
			//Load Form variables into NameValueCollection variable.
			coll = HttpContext.Current.Request.Form;

			// Get names of all forms into a string array.
			String[] requestItem = coll.AllKeys;

			for (i = 0; i < requestItem.Length; i++) {
				sArray.Add(requestItem[i], HttpContext.Current.Request.Form[requestItem[i]]);
			}

			return sArray;
		}

		/// <summary>
		/// 获取待签名字符串（调试用）
		/// </summary>
		/// <param name="inputPara">通知返回参数数组</param>
		/// <returns>待签名字符串</returns>
		private string GetPreSignStr(SortedDictionary<string, string> inputPara) {
			Dictionary<string, string> sPara = new Dictionary<string, string>();

			//过滤空值、sign与sign_type参数
			sPara = Core.FilterPara(inputPara);

			//获取待签名字符串
			string preSignStr = Core.CreateLinkString(sPara);

			return preSignStr;
		}

		/// <summary>
		/// 获取返回时的签名验证结果
		/// </summary>
		/// <param name="inputPara">通知返回参数数组</param>
		/// <param name="sign">对比的签名结果</param>
		/// <returns>签名验证结果</returns>
		private bool GetSignVeryfy(SortedDictionary<string, string> inputPara, string sign) {
			Dictionary<string, string> sPara = new Dictionary<string, string>();

			//过滤空值、sign与sign_type参数
			sPara = Core.FilterPara(inputPara);

			//获取待签名字符串
			string preSignStr = Core.CreateLinkString(sPara);

			//获得签名验证结果
			bool isSgin = false;
			if (sign != null && sign != "") {
				switch (_sign_type) {
					case "MD5":
						isSgin = AlipayMD5.Verify(preSignStr, sign, _key, _input_charset);
						break;
					case "RSA":
						isSgin = RSAFromPkcs8.Verify(preSignStr, sign, _key, _input_charset);
						break;
					default:
						break;
				}
			}

			return isSgin;
		}

		/// <summary>
		/// 获取是否是支付宝服务器发来的请求的验证结果
		/// </summary>
		/// <param name="notify_id">通知验证ID</param>
		/// <returns>验证结果</returns>
		private string GetResponseTxt(string notify_id) {
			string veryfy_url = Https_veryfy_url + "partner=" + _partner + "&notify_id=" + notify_id;

			//获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求
			string responseTxt = Get_Http(veryfy_url, 120000);

			return responseTxt;
		}

		/// <summary>
		/// 获取远程服务器ATN结果
		/// </summary>
		/// <param name="strUrl">指定URL路径地址</param>
		/// <param name="timeout">超时时间设置</param>
		/// <returns>服务器ATN结果</returns>
		private string Get_Http(string strUrl, int timeout) {
			string strResult;
			try {
				HttpWebRequest myReq = (HttpWebRequest)HttpWebRequest.Create(strUrl);
				myReq.Timeout = timeout;
				HttpWebResponse HttpWResp = (HttpWebResponse)myReq.GetResponse();
				Stream myStream = HttpWResp.GetResponseStream();
				StreamReader sr = new StreamReader(myStream, Encoding.Default);
				StringBuilder strBuilder = new StringBuilder();
				while (-1 != sr.Peek()) {
					strBuilder.Append(sr.ReadLine());
				}

				strResult = strBuilder.ToString();
			} catch (Exception exp) {
				strResult = "错误：" + exp.Message;
			}

			return strResult;
		}
	}
}